package com.googlecode.transmutant.spring

import org.springframework.beans.factory.config.BeanDefinition 
import org.springframework.beans.factory.support.AbstractBeanDefinition
import org.springframework.beans.factory.support.BeanDefinitionBuilder 
import org.springframework.beans.factory.support.ManagedList 
import org.springframework.beans.factory.support.ManagedMap 
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser 
import org.springframework.beans.factory.xml.ParserContext
import org.springframework.beans.factory.xml.XmlReaderContext 
import org.springframework.util.xml.DomUtils 
import org.w3c.dom.Element

class StepsBeanDefinitionParser extends AbstractBeanDefinitionParser {
	
	AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
		return parseStepsElement(element, parserContext)
	}
	
	static AbstractBeanDefinition parseStepsElement(Element element, ParserContext parserContext) {
		BeanDefinitionBuilder factory = BeanDefinitionBuilder.rootBeanDefinition(StepsFactoryBean.class)
		factory.addPropertyValue("stepsWrapper", parseSteps(element))
		List<Element> childElements = DomUtils.getChildElementsByTagName(element, "step")
		parseStepElements(childElements, factory, parserContext)
		return factory.getBeanDefinition()
	}
	
	static AbstractBeanDefinition parseSteps(Element element) {
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(StepsWrapper.class)
		return builder.getBeanDefinition()
	}
	
	static void parseStepElements(List<Element> childElements, BeanDefinitionBuilder factory, ParserContext parserContext) {
		ManagedList<BeanDefinition> children = new ManagedList<BeanDefinition>(childElements.size())
		for (Element element : childElements) {
			children.add(parseStepElement(element, parserContext))
		}
		factory.addPropertyValue("stepWrappers", children)
	}
	
	static AbstractBeanDefinition parseStepElement(Element element, ParserContext parserContext) {
		BeanDefinitionBuilder factory = BeanDefinitionBuilder.rootBeanDefinition(StepFactoryBean.class)
		def name = element.getAttribute("name")
		factory.addPropertyValue("name", name)
		
		if(element.hasAttribute("ref")) {
			factory.addPropertyReference("ref", element.getAttribute("ref"))
		} else if (element.hasAttribute("class")) {
			factory.addPropertyValue("clazz", element.getAttribute("class"))
		} else if(element.hasAttribute("capture")) {
		factory.addPropertyValue("capture", element.getAttribute("capture"))
		} else {
			throw new RuntimeException("'class', 'ref' or 'capture' attribute must exist on transmutant:step, name=" + name)
		}
		
		List<Element> methodElements = DomUtils.getChildElementsByTagName(element, "stepMethod")
		ManagedList<BeanDefinition> methods = new ManagedList<BeanDefinition>(methodElements.size())
		methodElements.each {e->
			methods.add(parseStepMethodElement(e, parserContext))
		}
		factory.addPropertyValue("stepMethods", methods)
		
		List<Element> captureElements = DomUtils.getChildElementsByTagName(element, "capture")
		factory.addPropertyValue("captures", getCaptures(captureElements));
		
		List<Element> inElements = DomUtils.getChildElementsByTagName(element, "in")
		ManagedList<BeanDefinition> ins = new ManagedList<BeanDefinition>(inElements.size())
		inElements.each{Element i->
			def captureName = i.getAttribute("capture")
			def retain = i.getAttribute("retain")
			def property = i.getAttribute("property")
			Capture capture = new Capture(captureName,Boolean.valueOf(retain),property)
			ins.add(capture)
		}
		factory.addPropertyValue("ins", ins)
		
		return factory.getBeanDefinition()
	}
	
	static List<Capture> getCaptures(List<Element> captureElements) {
		ManagedList<BeanDefinition> captures = new ManagedList<BeanDefinition>(captureElements.size())
		captureElements.each {Element element->
			def name = element.getAttribute("name")
			def retain = element.getAttribute("retain")
			def property = element.getAttribute("property")
			Capture capture = new Capture(name,Boolean.valueOf(retain),property)
			captures.add(capture)
		}
		return captures;
	}
	
	static AbstractBeanDefinition parseStepMethodElement(Element element, ParserContext parserContext) {
		BeanDefinitionBuilder factory = BeanDefinitionBuilder.rootBeanDefinition(StepMethodFactoryBean.class)
		def name = element.getAttribute("name")
		factory.addPropertyValue("name", name)
		
		if(element.hasAttribute("out")) {
			def out = element.getAttribute("out")
			factory.addPropertyValue("out", out)
		}
		
		if(element.hasAttribute("capture")) {
			def capture = element.getAttribute("capture")
			factory.addPropertyValue("capture", capture)
		}
		
		String [] childNames = ["in","ref"]
		List<Element> inElements = DomUtils.getChildElementsByTagName(element,childNames);
		ManagedList<BeanDefinition> ins = new ManagedList<BeanDefinition>(inElements.size())
		inElements.each {Element e->
			if(e.getLocalName().equals("in")) {
				def inName = e.getAttribute("capture")
				def retain = e.getAttribute("retain")
				InParam inParam = new InParam()
				inParam.name = inName
				inParam.retain = Boolean.valueOf(retain)
				ins.add(inParam)
			} else {
				def refBean = e.getAttribute("bean")
				RefParam refParam = new RefParam()
				refParam.bean = refBean
				ins.add(refParam)
			}
		}
		factory.addPropertyValue("params",ins)
		
		Element captureElement = DomUtils.getChildElementByTagName(element, "capture")
		if(captureElement!=null) {
			def captureName = captureElement.getAttribute("name")
			def retain = captureElement.getAttribute("retain")
			Capture capture = new Capture(captureName,Boolean.valueOf(retain),null)
			factory.addPropertyValue("capture", capture)
		}
		
		return factory.getBeanDefinition()
	}
}